package db
import(
	"crypto/md5"
	"database/sql"
	"encoding/json"
	"fmt"
	_"github.com/mattn/go-sqlite3"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)
// Handle Error
func Err(reason string,err error){
	log.Printf("%s:%v\n",reason,err)
}

// Encrypt Method
const loginSalt = "EL PSY CONGROO"
const sessionSalt = "The World"
func Encrypt(toEncrypt string,salt string)string{
	data := []byte(toEncrypt+salt)
	ha := md5.New()
	sumed := ha.Sum(data)
	return string(fmt.Sprintf("%x",sumed))
}

// Connect To Database
func Connect()*sql.DB{
	db,err := sql.Open("sqlite3","./cmoe-login.db")
	if err != nil{
		log.Println("<< Failed to connect to database:",err)
		return nil
	}
	return db
}

// Disconnect From Database
func Disconnect(db *sql.DB)bool{
	if err:=db.Close(); err != nil{
		log.Println("<< Failed to close database connection:",err)
		return false
	}
	return true
}

/*
	## User Data Stuct
 */

// User Login Struct
type UserLogin struct {
	Username string `json:"string"`
	PassWd string `json:"string"`
}
type LoginStatus struct{
	Status string `json:"login_status"`
	SessionId string `json:"session_id"`
}

func (user UserLogin)VerifySession(w http.ResponseWriter,req *http.Request){
	db := Connect()
	defer Disconnect(db)
	session_id := req.URL.Query().Get("session_id")
	rows,err := db.Query("SELECT session_id FROM login_session WHERE session_id = ?",session_id)
	if err != nil{
		Err("<< Failed to query user's session_id",err)
		return
	}
	var session_id_db string;var count = 0
	for rows.Next() {
		count ++
		rows.Scan(&session_id_db)
	}
	if count == 0 {
		data,_ := json.Marshal(&LoginStatus{Status:"session id not exists"})
		w.Write(data)
		return
	}
	if session_id_db != session_id {
		data,_:=json.Marshal(&LoginStatus{Status:"wrong session id"})
		w.Write(data)
		return
	}
	data,_:=json.Marshal(&LoginStatus{Status:"ok",SessionId:session_id})
	w.Write(data)
}
func (user UserLogin)Login(w http.ResponseWriter,req *http.Request){
	// Predelete the session created last time
	preDelSession(user.Username)
	updateIp(user.Username,req.RemoteAddr)
	db := Connect()
	defer Disconnect(db)
	log.Printf("[%s]Try to login with {username:%s,passwd:%s}",req.RemoteAddr,user.Username,user.PassWd)

	rows,err := db.Query("SELECT passwd FROM chatman WHERE username = ?",user.Username)
	if err != nil{
		Err("<< Failed to query user's password",err)
		return
	}

	var passwd string;var count = 0
	for rows.Next() {
		count ++
		rows.Scan(&passwd)
	}
	if count == 0 {
		data,_ := json.Marshal(&LoginStatus{Status:"user not exists"})
		w.Write(data)
		return
	}
	if Encrypt(user.PassWd,loginSalt) != passwd {
		data,_:=json.Marshal(&LoginStatus{Status:"wrong password"})
		log.Printf("Real:%s:%s",user.Username,passwd)
		w.Write(data)
		return
	}
	session_id := user.CreateLoginSession(user.Username)
	data,_:=json.Marshal(&LoginStatus{Status:"ok",SessionId:session_id})
	w.Write(data)

}
func (user UserLogin)CreateLoginSession(username string)string{
	db := Connect()
	defer Disconnect(db)
	dt := getDateTime()
	session_id := Encrypt(username+dt,sessionSalt)
	log.Println("<< Created New Session:",session_id)
	delayTime := time.Hour * 24 * 3
	expiretime := getDelayTime(delayTime)
	go time.AfterFunc(delayTime,func(){
		db := Connect()
		defer Disconnect(db)
		_,err := db.Exec("DELETE FROM login_session WHERE session_id = ?",session_id)
		if err != nil{
			Err("<< Failed to delete login session from database",err)
			return
		}
	})
	_,err := db.Exec("INSERT INTO login_session(username, session_id, expiretime) VALUES (?,?,?)",username,session_id,expiretime)
	if err != nil{
		Err("<< Failed to insert login session into database",err)
		return ""
	}
	return session_id
}
func (user UserLogin)Logout(w http.ResponseWriter,req *http.Request){
	if !preDelSession(req.URL.Query().Get("username")){
		data := "{\"logout_status\":\"error\"}"
		w.Write([]byte(data))
		return
	}
	data := "{\"logout_status\":\"ok\"}"
	w.Write([]byte(data))
}

// User Register Struct
type UserRegister struct {
	// chatman <username,passwd,nickname,lastip,sex,registime>
	Username string `json:"username"`
	Passwd string `json:"passwd"`
	Nickname string `json:"nickname"`
	Sex int `json:"sex"`
	Lastip string `json:"lastip"`
	Registime string `json:"registime"`
}

type RegisterStatus struct{
	Status string `json:"register_status"`
}

func(ur UserRegister)Register(w http.ResponseWriter,req *http.Request){
	db := Connect()
	defer Disconnect(db)
	req.ParseForm()
	ur.Username = req.PostForm.Get("username")
	ur.Passwd = Encrypt(req.PostForm.Get("passwd"),loginSalt)
	ur.Nickname = req.PostForm.Get("nickname")
	ur.Sex,_ = strconv.Atoi(req.PostForm.Get("sex"))
	ur.Lastip = strings.Split(req.RemoteAddr,":")[0]
	ur.Registime = time.Now().Format("2006-01-02 15:04:05")
	if IsExists(ur.Username){
		data,_ := json.Marshal(&RegisterStatus{Status:"user exists"})
		w.Write(data)
		return
	}
	_,err := db.Exec("INSERT INTO chatman(username,passwd,nickname,lastip,sex,registime) VALUES(?,?,?,?,?,?)",ur.Username,ur.Passwd,ur.Nickname,ur.Lastip,ur.Sex,ur.Registime)
	if err != nil{
		Err("Failed to insert register data into chatman",err)
		data,_ := json.Marshal(&RegisterStatus{Status:"error"})
		w.Write(data)
		return
	}
	data,_ := json.Marshal(&RegisterStatus{Status:"ok"})
	w.Write(data)
}


/*
	## Utilities
*/
// Time Methods
const timeFormat = "2006-01-02 15:04:05"
func getDateTime()string{
	format := timeFormat
	return time.Now().Format(format)
}

func getDelayTime(d time.Duration)string{
	timenow := time.Now()
	unixdelay := timenow.Add(d)
	return unixdelay.Format(timeFormat)
}
// User Logout Struct
type UserLogout struct{

}

// Is User Exist
func IsExists(username string)bool{
	db := Connect()
	defer Disconnect(db)
	rows,err := db.Query("SELECT chatman.username FROM chatman WHERE username = ?",username)
	if err != nil{
		Err("Failed to query user list for isExits()",err)
		return true
	}
	var count = 0
	for rows.Next(){
		count++
	}
	if count == 0{
		return false
	}
	return true
}
func preDelSession(username string)bool{
	db := Connect()
	defer Disconnect(db)
	rows,err := db.Query("SELECT session_id FROM login_session WHERE username = ?",username)
	if err != nil{
		Err("Failed to query session list for preDelSession()",err)
		return false
	}
	var count = 0
	for rows.Next(){
		count++
	}
	if count == 0{
		return true
	}
	_,err  = db.Exec("DELETE FROM login_session WHERE username = ?",username)
	if err != nil{
		Err("Failed to delete session",err)
		return false
	}
	return true
}
func updateIp(username string,ip string)bool{
	db := Connect()
	defer Disconnect(db)
	_,err := db.Exec("UPDATE chatman SET lastip = ? WHERE username = ?",strings.Split(ip,":")[0],username)
	if err != nil{
		Err("Failed to update lastip for user: "+username,err)
		return false
	}
	return true
}